package com.chenfan.common.config;

import com.alibaba.fastjson.JSONObject;
import com.chenfan.common.extension.context.TraceContext;
import com.chenfan.common.extension.context.TraceContextHolder;
import com.chenfan.common.vo.UserVO;
import com.chenfan.infra.context.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Objects;

/**
 * @author wulg
 * @date 2024/3/28
 */
@Configuration
public class ApiGatewayAutoConfiguration {

    /**
     *
     * @param stringRedisTemplate
     * @return
     */
    @Bean
    public FilterRegistrationBean apiGatewayFilter(@Autowired StringRedisTemplate stringRedisTemplate){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new ApiGatewayFilter(stringRedisTemplate));
        filterRegistrationBean.setName("apiGatewayFilter");
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
        filterRegistrationBean.setEnabled(true);
        return filterRegistrationBean;
    }

    /**
     * API网关过滤-Token换取登录用户信息
     * @author wulg
     * @date 2021-09-28
     **/
    @SuppressWarnings("AlibabaRemoveCommentedCode")
    @Slf4j
    public static final class ApiGatewayFilter extends OncePerRequestFilter {

        private final static PathMatcher PATH_MATCHER = new AntPathMatcher();

        private final StringRedisTemplate stringRedisTemplate;

        private String[] excludePathPatterns;

        public ApiGatewayFilter(final StringRedisTemplate stringRedisTemplate) {
            this.stringRedisTemplate = stringRedisTemplate;
            //排除静态资源
            this.excludePathPatterns = new String[]{"/**/*.*"};
        }

        public ApiGatewayFilter(final StringRedisTemplate stringRedisTemplate, String... excludePathPatterns) {
            this.stringRedisTemplate = stringRedisTemplate;
            this.excludePathPatterns = excludePathPatterns;
        }

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            if(!this.toBeFiltered(request, response)) {
                filterChain.doFilter(request, response);
            } else {
                TraceContextHolder.putSceneEntry(TraceContext.SceneEntry.HTTP);
                String tenantId = request.getHeader(TraceContext.TENANT_ID);
                String token = request.getHeader(TraceContext.TOKEN);
                String appKey = request.getHeader(TraceContext.APP_KEY);
                UserVO userVO = null;
                try {
                    if(StringUtils.isNotBlank(token)) {
                        TraceContextHolder.put(TraceContext.TOKEN, token);
                    }
                    if(StringUtils.isNotBlank(appKey)) {
                        TraceContextHolder.put(TraceContext.APP_KEY, appKey);
                    }
                    /** 由于网关透传过来的用户信息不对，暂时注释掉  */
                    //userVO = this.getUserInfoFromApiGateway(request);
                    if(Objects.isNull(userVO) && StringUtils.isNotBlank(token)) {
                        userVO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(Constant.AUTHORIZATION + ":" + token), UserVO.class);
                    }
                    if(userVO != null) {
                        TraceContextHolder.put(TraceContext.CURRENT_USER, userVO);
                        TraceContextHolder.put(TraceContext.TENANT_ID, userVO.getTenantId());
                        TenantContextHolder.setTenantId(userVO.getTenantId());
                    } else if(StringUtils.isNotBlank(tenantId)){
                        log.info("查不到登录用户信息，从请求头中获取租户ID={}", tenantId);
                        TraceContextHolder.put(TraceContext.TENANT_ID, Long.valueOf(tenantId));
                        TenantContextHolder.setTenantId(Long.valueOf(tenantId));
                    } else {
                        log.error("请求头无租户ID且查不到登录用户信息！");
                    }
                    filterChain.doFilter(new ResettableHttpServletRequestWrapper(request), response);
                } finally {
                    TraceContextHolder.clear();
                    TenantContextHolder.clear();
                }
            }
        }

        /**
         * 获取经API网关鉴权后的用户信息
         * @param request
         * @return
         */
        private UserVO getUserInfoFromApiGateway(HttpServletRequest request) throws UnsupportedEncodingException {
            String encodedUserInfo = request.getHeader(TraceContext.CURRENT_USER);
            if(StringUtils.isNotBlank(encodedUserInfo)) {
                String decodedUserInfo = URLDecoder.decode(encodedUserInfo, "UTF-8");
                if(log.isDebugEnabled())  {
                    log.debug("获取到经API网关鉴权后的用户信息！解密报文：{}", decodedUserInfo);
                }
                return JSONObject.parseObject(decodedUserInfo, UserVO.class) ;

            }
            return null;
        }

        /**
         * 是否被过滤
         * @param request
         * @param response
         * @return
         */
        private boolean toBeFiltered(HttpServletRequest request, HttpServletResponse response) {
            boolean flag = true;
            if(ArrayUtils.isNotEmpty(excludePathPatterns)){
                String uri = request.getRequestURI();
                for(String excludeStaticPattern : excludePathPatterns){
                    if(!PATH_MATCHER.match(excludeStaticPattern, uri)){
                        continue;
                    }
                    flag = false;
                    break;
                }
            }
            return flag;
        }
    }

    /**
     * 已经迁移到infra
     * 重写请求封装类  待RequestParameterInterceptor做请求头扩展
     */
    public static class ResettableHttpServletRequestWrapper extends HttpServletRequestWrapper {

        public ResettableHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
        }

        @Override
        public String getHeader(String name) {
            String value = super.getHeader(name);
            if(Constant.PRIVILEGE_CODE.equals(name) && TraceContextHolder.contains(Constant.PRIVILEGE_CODE)) {
                Object attribute = TraceContextHolder.get(Constant.PRIVILEGE_CODE);
                value = Objects.nonNull(attribute) ? attribute.toString() : null;
            }
            return value;
        }
    }
}
